package com.maikang.exammall.service;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import com.maikang.exammall.entity.User;
import com.maikang.exammall.entity.UserContext;
import com.maikang.exammall.entity.constant.TokenConstant;
import com.maikang.exammall.entity.dto.WxEnterDTO;
import com.maikang.exammall.repository.UserRepository;
import com.maikang.exammall.util.TokenUtil;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.UUID;

@Service
@Log4j2
public class WxEnterService {

    // open_id.x123xua:{user_id: usr, mobile}
    // user_id_token.1232: token
    // token.xxx: {user_id: id, mobile: mobile}

    @Autowired
    private RedisService redisService;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private WxMpService wxMpService;

    @Autowired
    private TokenUtil tokenUtil;

    public WxEnterDTO maEnter(WxMaJscode2SessionResult session) {
        User user = userRepository.findByUnionId(session.getUnionid());
        if (user == null) {
            user = User.builder().maOpenId(session.getOpenid())
                    .unionId(session.getUnionid())
                    .build();
            user = userRepository.save(user);
        } else {
            if (!StringUtils.hasLength(user.getMaOpenId())) {
                user.setMaOpenId(session.getOpenid());
                user = userRepository.save(user);
            }
        }
        String token = TokenConstant.maTokenPrefix + UUID.randomUUID().toString().substring(3);
        String tokenUserIdKey = TokenConstant.getMaToken_userIdKeyFromToken(token);
        redisService.set(tokenUserIdKey, user.getId(), TokenConstant.maTokenExpireTime);
        return new WxEnterDTO(token, user.getMobile());
    }

    public void maUserInfo(WxMaUserInfo userInfo) {
        String gender = userInfo.getGender();
        Integer sex = gender.equals("男") ? 1 : gender.equals("女") ? 2 : 0;
        log.debug("gender is {}", gender);
        User user = UserContext.getUser();
        user.setAvatarUrl(userInfo.getAvatarUrl());
        user.setUsername(userInfo.getNickName());
        user.setCountry(userInfo.getCountry());
        user.setCity(userInfo.getCity());
        user.setGender(sex);
        user.setProvince(userInfo.getProvince());
        userRepository.save(user);
    }

    public void maPhone(WxMaPhoneNumberInfo phoneNoInfo) {
        User user = UserContext.getUser();
        if (StringUtils.hasLength(user.getMobile())) {
            log.info("user {} already bind phone, not going update", user);
            return;
        }
        user.setMobile(phoneNoInfo.getPurePhoneNumber());
        userRepository.save(user);
    }



    @SneakyThrows
    public WxEnterDTO enter(WxMpUser wxMpUser, WxOAuth2AccessToken accessToken, String code, Long refererId) {
        User user;
        // user exists in mysql
        user = userRepository.findByOpenId(accessToken.getOpenId()).orElse(null);
        if (user != null) {
            log.info("code: {}, wxUserInfo: {}, user from mysql {} ", code, wxMpUser, user);
            String token = handleUserExists(user, wxMpUser);
            return new WxEnterDTO(token, user.getMobile());
        }

        // user Does not exists in mysql
        if (!wxMpUser.getSubscribe()) {
            WxOAuth2UserInfo wxOAuth2UserInfo = wxMpService.getOAuth2Service().getUserInfo(accessToken, "zh_CN");
            log.info("not exists in mysql, has not subscribed, code: {}, wxUserInfo: {}, wxOAuth2UserInfo: {}", code, wxMpUser, wxOAuth2UserInfo);
            user = User.builder().unsubscribed(true)
                    .refererId(refererId)
                    .username(wxOAuth2UserInfo.getNickname())
                    .avatarUrl(wxOAuth2UserInfo.getHeadImgUrl())
                    .gender(wxOAuth2UserInfo.getSex())
                    .country(wxOAuth2UserInfo.getCountry())
                    .province(wxOAuth2UserInfo.getProvince())
                    .city(wxOAuth2UserInfo.getCity())
                    .openId(wxOAuth2UserInfo.getOpenid())
                    .unionId(wxOAuth2UserInfo.getUnionId())
                    .build();
        } else {
            log.info("not exists in mysql, has subscribed code: {}, wxUserInfo: {}", code, wxMpUser);
            user = User.builder().username(wxMpUser.getNickname())
                    .avatarUrl(wxMpUser.getHeadImgUrl())
                    .gender(wxMpUser.getSex())
                    .country(wxMpUser.getCountry())
                    .province(wxMpUser.getProvince())
                    .city(wxMpUser.getCity())
                    .openId(accessToken.getOpenId())
                    .unionId(wxMpUser.getUnionId())
                    .build();
        }
        log.info("code: {}, wxUserInfo: {}, going to physically create a new user in mysql: {} ", code, wxMpUser, user);
        userRepository.save(user);

        // 设置 user_id_token.1232: token
        // 设置 token.xxx: {user_id: id, mobile: mobile}
        String token = UUID.randomUUID().toString();
        tokenUtil.resetToken(token, user);
        return new WxEnterDTO(token, "");
    }

    // 设置 user_id_token.1232: token
    // 设置 token.xxx: {user_id: id, mobile: mobile}
    private String handleUserExists(User user, WxMpUser wxMpUser) {
        if (!StringUtils.hasLength(user.getUnionId()) && StringUtils.hasLength(wxMpUser.getUnionId())) {
            log.info("user {} exists, but has no union id, going to update it with {}", user, wxMpUser.getUnionId());
            user.setUnionId(wxMpUser.getUnionId());
            userRepository.save(user);
        }
        final String userIdTokenKey = TokenConstant.getUserId_TokenKeyFromUserId(user.getId());
        String token;
        Object tokenFromRedis = redisService.get(userIdTokenKey);
        if (tokenFromRedis == null) {
            log.info("token expired, userId is {}, mobile is {}", user.getId(), user.getMobile());
            token = UUID.randomUUID().toString();
        } else {
            token = (String) tokenFromRedis;
        }
        tokenUtil.resetToken(token, user);
        log.info("token for user {} is {}", user, token);
        return token;
    }


}
